package com.tom.demo.common.exception;

import cn.dev33.satoken.exception.*;
import com.tom.demo.common.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.util.List;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 拦截：未登录异常 http状态码 统一返回401
     * @return
     */
    @ExceptionHandler(NotLoginException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result handlerException(NotLoginException e,HttpServletRequest request) {
        log.error("NotLoginException start, uri:{}, exception:{}, caused by: {}",request.getRequestURI(),e.getCode(),e.getMessage());
        // 不同异常返回不同信息
        String message;
        if (e.getType().equals(NotLoginException.NOT_TOKEN)) {
            // -1 未能读取到有效 token
            message = NotLoginException.NOT_TOKEN_MESSAGE;
        } else if (e.getType().equals(NotLoginException.INVALID_TOKEN)) {
            // -2 token 无效
            message = NotLoginException.INVALID_TOKEN_MESSAGE;
        } else if (e.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
            // -3
            message = "登录信息已过期，请重新登录";
        } else if (e.getType().equals(NotLoginException.BE_REPLACED)) {
            // -4
            message = "您的账户已在另一台设备上登录，如非本人操作，请立即修改密码";
        } else if (e.getType().equals(NotLoginException.KICK_OUT)) {
            // -5
            message = "已被系统强制下线";
        } else {
            message = "未登录,请先登录";
        }
        // 返回给前端
        return Result.error(message,401);
    }


    // 拦截：缺少权限异常
    @ExceptionHandler(NotPermissionException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result handlerException(NotPermissionException e,HttpServletRequest request) {

        log.error("NotPermissionException start, uri:{}, exception:{}, caused by: {}",request.getRequestURI(),e.getCode(),e.getMessage());

        return Result.error("缺少权限：" + e.getPermission(),403);
    }

    // 拦截：缺少角色异常
    @ExceptionHandler(NotRoleException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result handlerException(NotRoleException e,HttpServletRequest request) {

        log.error("NotRoleException start, uri:{}, exception:{}, caused by: {}",request.getRequestURI(),e.getCode(),e.getMessage());

        return Result.error("缺少角色：" + e.getRole(),403);
    }

    // 拦截：二级认证校验失败异常 403
    @ExceptionHandler(NotSafeException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result handlerException(NotSafeException e,HttpServletRequest request) {

        log.error("NotSafeException start, uri:{}, exception:{}, caused by: {}",request.getRequestURI(),e.getCode(),e.getMessage());


        return Result.error("二级认证校验失败：" + e.getService(),403);
    }

    // 拦截：服务封禁异常 403
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(DisableServiceException.class)
    public Result handlerException(DisableServiceException e,HttpServletRequest request) {

        log.error("DisableServiceException start, uri:{}, exception:{}, caused by: {}",request.getRequestURI(),e.getCode(),e.getMessage());


        return Result.error("当前账号 " + e.getService() + " 服务已被封禁 (level=" + e.getLevel() + ")：" + e.getDisableTime() + "秒后解封",403);
    }


    /**
     * 处理可控异常  自定义异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public Result customException(CustomException e, HttpServletRequest request){
        log.error("customException自定义异常 start, uri:{}, exception:{}, caused by: {}",request.getRequestURI(),e.getCode(),e.getMessage());
        return Result.error(e.getMessage(),e.getCode());
    }

    /**
     * 全局系统异常，未知异常统一处理
     */
    @ExceptionHandler(value = Exception.class)
    public Result errorHandler(Exception e, HttpServletRequest request) {
        if (e instanceof MissingServletRequestParameterException) {
            log.error("MissingServletRequestParameterException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("缺少参数",5003);
        } else if (e instanceof TypeMismatchException) {
            log.error("TypeMismatchException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("参数类型匹配失败",5004);
        } else if(e instanceof HttpMessageNotReadableException){
            log.error("HttpMessageNotReadableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("JSON数据序列化为对象时发生错误",5005);
        } else if(e instanceof HttpMediaTypeNotSupportedException){
            log.error("HttpMediaTypeNotSupportedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("请求Content-type类型不支持",5006);
        } else if(e instanceof HttpMessageNotWritableException){
            log.error("HttpMessageNotWritableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("对象序列化为JSON时发生错误",5007);
        } else if(e instanceof HttpRequestMethodNotSupportedException){
            log.error("HttpRequestMethodNotSupportedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("请求方式不支持",5008);
        } else if(e instanceof MissingPathVariableException){
            log.error("MissingPathVariableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("未检测到路径参数",5009);
        } else if(e instanceof MethodArgumentNotValidException){
            String errorMsg = getErrorMsg((MethodArgumentNotValidException) e, request);
            return Result.error(errorMsg);
        } else if(e instanceof BindException){
            log.error("BindException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("未检测到查询参数",5010);
        } else if (e instanceof SQLException) {
            log.error("SQLException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("数据库操作失败",5011);
        } else if (e instanceof MaxUploadSizeExceededException) {
            log.error("MaxUploadSizeExceededException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("文件上传超出大小限制",5012);
        } else if (e instanceof SocketTimeoutException) {
            log.error("SocketTimeoutException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("socket服务连接超时",5013);
        } else if (e instanceof SocketException) {
            log.error("SocketException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("socket服务连接失败",5014);
        } else if (e instanceof IOException) {
            log.error("IOException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("文件IO读写错误",5015);
        }else if(e instanceof RuntimeException){
            log.error("RuntimeException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("服务器内部错误",5000);
        }  else {
            log.error("Exception error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("系统错误");
        }
    }

    @NotNull
    private static String getErrorMsg(MethodArgumentNotValidException e, HttpServletRequest request) {
        BindingResult result = e.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder();
        List<ObjectError> errors = result.getAllErrors();
        errors.forEach(p -> {
            FieldError fieldError = (FieldError) p;
            log.error("MethodArgumentNotValidException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            stringBuilder.append(fieldError.getDefaultMessage());
        });
        return stringBuilder.toString();
    }
}
